/**
 * File Name: produce_consumer_01.c
 * Auther: Jichang
 * Environment: Fedora 7 server
 */
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
#include<pthread.h>

// error thread to quit.
void err_thread(int ret, char *str)
{
	if(ret != 0) {
		fprintf(stderr, "%s: %s\n", str, strerror(ret));
		pthread_exit(NULL);
	}
}

// linklist.
struct msg {
	int num;
	struct msg *next;
};
struct msg *head;

//init mutex and a condition-var
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t has_produce = PTHREAD_COND_INITIALIZER; // considtion var

void *producer(void *arg)
{
	int ret;
	while(1){ 
		struct msg *mp = malloc(sizeof(struct msg));

		// simulate produce
		mp->num = rand() % 1000 + 1;
		printf("--Produce %d\n", mp->num);

		ret = pthread_mutex_lock(&mutex);
		if(ret != 0) {
			err_thread(ret, "pro_lock err");
		}
		mp->next = head;
		head = mp; //write
		ret = pthread_mutex_unlock(&mutex);
		if(ret != 0) {
			err_thread(ret, "pro_unlock err");
		}

		// send signal
		pthread_cond_signal(&has_produce);

		sleep(rand() % 3);
	}
	return NULL;
}

void *consumer(void *arg)
{
	int ret;
	while(1) { 
		struct msg *mp;

		ret = pthread_mutex_lock(&mutex);
		if(ret != 0) {
			err_thread(ret, "consu_lock err");
		}
		if(head == NULL) {
			// block wait cndition.
			pthread_cond_wait(&has_produce, &mutex);
		}

		// read
		mp = head;
		head = mp->next;

		ret = pthread_mutex_unlock(&mutex);
		if(ret != 0) {
			err_thread(ret, "consu_unlock error");
		}
		printf("==Consumer id: %lu: %d\n", pthread_self(), mp->num);

		free(mp);
		sleep(rand() % 3);
	}

	return NULL;
}

int main(int argc, char *argv[])
{
	pthread_t pid, cid;
	int ret;
	srand(time(NULL));

	ret = pthread_create(&pid, NULL, producer, NULL);
	if(ret != 0) {
		err_thread(ret, "pthread_error");
	}
	ret = pthread_create(&cid, NULL, consumer, NULL);
	if(ret != 0) {
		err_thread(ret, "pthread_error");
	}

	// reclaim
	pthread_join(pid, NULL);
	pthread_join(cid, NULL);

	return 0;
}

